﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Text.RegularExpressions;
using System.Threading;
using UDS.Management.SMS;
using UDS.Management.SMSv2;

namespace UDS.Management
{
    internal class SmsSenderService : IDisposable
    {
        private ISmsSender _sender;
        private Thread _senderThread;
        private bool _terminated = false;
        private string _connectionString;
        private string _smsPrefix;
        private string _smsSuffix;

        /// <summary>
        /// Create an instance of SmsSenderService.
        /// </summary>
        public SmsSenderService()
        {
            // Initialize the SmsSender
            NameValueCollection appSettings = ConfigurationManager.AppSettings;
            if (string.IsNullOrEmpty(appSettings["ConnectionString"]))
            {
                // The application should be running as a windows servive
                string exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
                Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);

                appSettings = new NameValueCollection();
                foreach (string key in config.AppSettings.Settings.AllKeys)
                {
                    appSettings[key] = config.AppSettings.Settings[key].Value;
                }
            }
            // _sender = new SmsSender(appSettings);
            // _sender.SmsCenter = appSettings["SMS_Center"];
            _sender = new NativeSmsSender(appSettings);

            // Read additional configs
            _connectionString = appSettings["ConnectionString"];
            if (string.IsNullOrEmpty(_connectionString))
            {
                Logger.WriteLine("ConnectionString is not configured!", Severity.Error);
                throw new InvalidOperationException("ConnectionString is not configured!");
            }
            _smsPrefix = appSettings["SMS_Prefix"];
            _smsSuffix = appSettings["SMS_Suffix"];

            // Create the Sender thread
            _senderThread = new Thread(SenderThreadProc);
            _senderThread.Name = "SMS Sender Thread";
            _senderThread.IsBackground = true;
        }

        public void Start()
        {
            Logger.WriteLine("Starting UDS Management Service...");
            _sender.Open();
            _senderThread.Start();
        }

        public void Stop()
        {
            Logger.WriteLine("Stopping UDS Management Service...");
            _terminated = true;

            _senderThread.Join(3000);
        }

        public void Dispose()
        {
            _sender.Dispose();
        }

        private void SenderThreadProc()
        {
            while (!_terminated)
            {
                try
                {
                    // Get a list of SMSs to send
                    using (SqlConnectionHolder holder = new SqlConnectionHolder(_connectionString))
                    {
                        SmsItem[] list = SmsItem.GetSMSList(holder);

                        foreach (SmsItem item in list)
                        {
                            bool succeeded = true;
                            string msg = NormalizeSMS(item.Message);
                            if (!string.IsNullOrEmpty(msg))
                            {
                                string validatedPhone = ValidateMobilePhone(item.ReceiverPhone);
                                if (!string.IsNullOrEmpty(validatedPhone))
                                {
                                    System.Text.StringBuilder msgBuilder = new System.Text.StringBuilder();
                                    if (!string.IsNullOrEmpty(_smsPrefix))
                                    {
                                        msgBuilder.Append(_smsPrefix);
                                    }
                                    msgBuilder.Append(msg);
                                    if (!string.IsNullOrEmpty(_smsSuffix))
                                    {
                                        msgBuilder.Append(_smsSuffix);
                                    }

                                    succeeded = _sender.SendSMS(item.ReceiverPhone, msgBuilder.ToString());
                                }
                                else
                                {
                                    Logger.WriteLine(string.Format("Failed to send SMS to {0} since he/she doesn't have a valid mobile phone number.", item.Receiver));
                                }
                            }

                            // Update the SMS status to the database
                            item.UpdateSMSStatus(holder, succeeded);

                            if (_terminated)
                            {
                                break;
                            }
                        }
                    }
                }
                catch (Exception exp)
                {
                    Logger.WriteLine(string.Format("Unknown error: {0}", exp), Severity.Error);
                }

                if (!_terminated)
                {
                    Thread.Sleep(2000);
                }
            }
        }

        private static string ValidateMobilePhone(string phone)
        {
            if (string.IsNullOrEmpty(phone) || phone.Length < 11)
                return string.Empty;

            phone = Regex.Replace(phone, @"[^\d]", string.Empty);
            if (phone.Length == 13)
                return phone;

            if (phone.Length == 11)
                return "86" + phone;

            return string.Empty;
        }

        private static string NormalizeSMS(string msg)
        {
            if (string.IsNullOrEmpty(msg))
            {
                return string.Empty;
            }

            // Remove xml/html tags
            msg = Regex.Replace(msg, @"\<[^\>]*\>", string.Empty, RegexOptions.Compiled | RegexOptions.Singleline);

            // Replace all invisible characters as a space
            msg = Regex.Replace(msg, @"\s", "\u0020", RegexOptions.Compiled);

            // Remove redundant spaces
            msg = Regex.Replace(msg, @"\s{2,}", "\u0020", RegexOptions.Compiled);

            return msg.Trim();
        }
    }
}
